# -*- coding: UTF-8 -*-
# Rabin.py
# Rabin加密算法
"""
Created on 2021

@author: Ximing
"""

import random
import math


# 定义素数判定函数
def isPrime(n):
    if n <= 1:
        return False
    for i in range(2, int(math.sqrt(n)) + 1):
        if n % i == 0:
            return False
    return True


# 定义密钥生成函数，生成公私钥
def Rabin_getkeys():
    pks = [i for i in range(130, 200) if isPrime(i) and i % 4 == 3]
    p = random.choice(pks)
    q = p
    while 1 == 1:
        if p == q:
            q = random.choice(pks)
        else:
            break
    n = p * q
    keys = [p, q, n]
    return keys


# 定义加密函数
def Rabin_encryption(message, pk):
    pt = message
    ct = []
    for i in pt:
        ct.append(str(hash(str(ord(i)))))
        ct.append(str(ord(i) * ord(i) % pk))
    return ct


# 定义解密函数
def Rabin_decryption(message, p, q):
    ciphertext = message
    plaintext = []
    temp = 0
    for i in range(p):
        if (i * q) % p == 1:
            temp = i
            break
    a = q * temp  # a =  y_p
    temp = 0
    for i in range(q):
        if (i * p) % q == 1:
            temp = i
            break
    b = p * temp
    for i in range(len(ciphertext)):
        if i % 2 == 1:
            m_temp = []
            m = []
            print('单个密文：', end="")
            print(ciphertext[i])
            m_temp.append(pow(int(ciphertext[i]), (p + 1) // 4) % p)
            m_temp.append(p - pow(int(ciphertext[i]), (p + 1) // 4) % p)
            m_temp.append(pow(int(ciphertext[i]), (q + 1) // 4) % q)
            m_temp.append(q - pow(int(ciphertext[i]), (q + 1) // 4) % q)
            m.append((a * m_temp[0] + b * m_temp[2]) % (p * q))
            m.append((a * m_temp[0] + b * m_temp[3]) % (p * q))
            m.append((a * m_temp[1] + b * m_temp[2]) % (p * q))
            m.append((a * m_temp[1] + b * m_temp[3]) % (p * q))
            print('单个解密的明文：', end="")
            print(m)
            for each_m in m:
                if hash(str(each_m)) == int(ciphertext[i - 1]):
                    print("正确的明文(ascii 整数)：", end="")
                    print(each_m)
                    plaintext.append(chr(each_m))

    return ''.join(plaintext)


if __name__ == '__main__':
    print("随机产生公钥与私钥，前两位为私钥用于解密，最后一位为公钥，用于加密:")
    keys = Rabin_getkeys()
    # 打印公钥与私钥，前两位为私钥用于解密，最后一位为公钥，用于加密
    print(keys)

    # 打印加密结果
    print("===============以下是加密的结果================")
    plaintext = 'We all love SCAU'
    cypher = Rabin_encryption(plaintext, keys[2])
    print("明文是:"+plaintext)
    print("输出密文，前一位为hash值，后一位为密文（字符串中每个字符对应ascii整数的加密值）")
    for i in range(len(cypher)):
        if (i % 2 == 0):
            print(cypher[i], end="")
        else:
            print(": " + str(cypher[i]))
    # 打印解密结果
    print("=============以下是解密的过程展示===============")
    dePlaintext = Rabin_decryption(cypher, keys[0], keys[1])
    print("=============以下是解密的结果===============")
    print(dePlaintext)
